package com.ragdollcat.thirdaicodegenerate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.ragdollcat.thirdaicodegenerate.ai.CodeFileFacade;
import com.ragdollcat.thirdaicodegenerate.ai.strategy.FluxHandlerStrategy;
import com.ragdollcat.thirdaicodegenerate.api.ScreenshotAPI;
import com.ragdollcat.thirdaicodegenerate.api.UserAPI;
import com.ragdollcat.thirdaicodegenerate.common.BaseResponse;
import com.ragdollcat.thirdaicodegenerate.common.ResultUtils;
import com.ragdollcat.thirdaicodegenerate.constants.AppConstant;
import com.ragdollcat.thirdaicodegenerate.core.build.BuildVueProject;
import com.ragdollcat.thirdaicodegenerate.enums.ChatHistoryMessageTypeEnum;
import com.ragdollcat.thirdaicodegenerate.enums.CodeGenTypeEnum;
import com.ragdollcat.thirdaicodegenerate.exception.BusinessException;
import com.ragdollcat.thirdaicodegenerate.exception.ErrorCode;
import com.ragdollcat.thirdaicodegenerate.exception.ThrowUtils;
import com.ragdollcat.thirdaicodegenerate.mapper.AppMapper;
import com.ragdollcat.thirdaicodegenerate.model.dto.app.AppDeployRequest;
import com.ragdollcat.thirdaicodegenerate.model.dto.app.AppQueryRequest;
import com.ragdollcat.thirdaicodegenerate.model.entity.App;
import com.ragdollcat.thirdaicodegenerate.model.entity.User;
import com.ragdollcat.thirdaicodegenerate.model.vo.app.AppVO;
import com.ragdollcat.thirdaicodegenerate.model.vo.user.UserVO;
import com.ragdollcat.thirdaicodegenerate.service.AppService;
import com.ragdollcat.thirdaicodegenerate.service.ChatHistoryService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author Administrator
 */
@Slf4j
@Service
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @Resource
    @Lazy
    private UserAPI userService;

    @Resource
    @Lazy
    private CodeFileFacade codeFileFacade;

    @Resource
    @Lazy
    private ChatHistoryService chatHistoryService;


    @Resource
    private FluxHandlerStrategy fluxHandlerStrategy;

    @Resource
    private BuildVueProject buildVueProject;

    @Resource
    @Lazy
    private ScreenshotAPI screenShotsService;


    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if (appQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create()
                .eq("id", id)
                .like("appName", appName)
                .like("cover", cover)
                .like("initPrompt", initPrompt)
                .eq("codeGenType", codeGenType)
                .eq("deployKey", deployKey)
                .eq("priority", priority)
                .eq("userId", userId)
                .orderBy(sortField, "ascend".equals(sortOrder));
    }


    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        // 关联查询用户信息
        Long userId = app.getUserId();
        if (userId != null) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            appVO.setUser(userVO);
        }
        return appVO;
    }


    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        if (CollUtil.isEmpty(appList)) {
            return new ArrayList<>();
        }
        // 批量获取用户信息，避免 N+1 查询问题
        Set<Long> userIds = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toSet());
        Map<Long, UserVO> userVOMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, userService::getUserVO));
        return appList.stream().map(app -> {
            AppVO appVO = getAppVO(app);
            UserVO userVO = userVOMap.get(app.getUserId());
            appVO.setUser(userVO);
            return appVO;
        }).collect(Collectors.toList());
    }

    /**
     * 对话并生成文件
     *
     * @param appId
     * @param message
     * @param user
     * @return
     */
    @Override
    public Flux<String> chatToGenCode(Long appId, String message, User user) {
        App app = getById(appId);
        ThrowUtils.throwIf(ObjUtil.isEmpty(app), ErrorCode.OPERATION_ERROR, "未查询到应用！");
        ThrowUtils.throwIf(!app.getUserId().equals(user.getId()), ErrorCode.NO_AUTH_ERROR, "您只能操作自己的应用！");

        //代码类型枚举
        String codeGenType = app.getCodeGenType();

        CodeGenTypeEnum enumByValue = CodeGenTypeEnum.getEnumByValue(codeGenType);
        ThrowUtils.throwIf(ObjUtil.isEmpty(enumByValue), ErrorCode.OPERATION_ERROR, "代码生成类型非法！");

        chatHistoryService.saveChatHistory(message, appId, user.getId(), ChatHistoryMessageTypeEnum.USER.getValue());
        log.info("保存用户对话历史成功");

        //监控相关，与AI对话前，首先将用户信息和AppId，存入线程上下文类中
//        MonitorContextHolder.setContext(MonitorContext.builder().userId(user.getId()).appId(appId).build());

        //与AI对话 并保存生成的文件到本地
        Integer version = app.getVersion();
        Flux<String> flux = codeFileFacade.callAiAndSaveWithFlux(message, codeGenType, appId, version - 1);


        //这里需要进行分派，根据一般类型（html或多文件）以及特殊类型（vue-project）
        //保存AI对话记录，报错也要保存
        //对flux进行处理，解决空格的问题，通过将结果转化为json
        //合并处理逻辑到同一个Flux链
        return fluxHandlerStrategy.fluxHandler(codeGenType, flux, chatHistoryService,appId, user, (version -1 ));
//                .doFinally(signalType -> MonitorContextHolder.clearContext());

    }

    /**
     * 部署应用
     *
     * @param appDeployRequest
     * @param user
     * @return
     */
    @Override
    public BaseResponse<String> deployApp(AppDeployRequest appDeployRequest, User user) {
        Long appId = appDeployRequest.getAppId();
        App app = getById(appId);
        ThrowUtils.throwIf(ObjUtil.isEmpty(app), ErrorCode.OPERATION_ERROR, "未查询到应用！");
        ThrowUtils.throwIf(!app.getUserId().equals(user.getId()), ErrorCode.NO_AUTH_ERROR, "您只能操作自己的应用！");

        //代码类型枚举
        String codeGenType = app.getCodeGenType();

        CodeGenTypeEnum enumByValue = CodeGenTypeEnum.getEnumByValue(codeGenType);
        ThrowUtils.throwIf(ObjUtil.isEmpty(enumByValue), ErrorCode.OPERATION_ERROR, "代码生成类型非法！");

        //查看目前是否有部署key
        String deployKey = app.getDeployKey();
        //如果当前没有部署key，就生成一个
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }

        //查找目前应用是否已经保存在本地
        //D:\Idea_workspace\2025\third-ai-code-generate\tmp\code\multi_file_334247392819560448_1
        //这里的版本号需要 - 1 因为文件保存成功后，版本号会 + 1
        String savePath = AppConstant.SAVE_PATH + codeGenType + "_" + appId + "_" + (app.getVersion() - 1);

        File file = new File(savePath);
        ThrowUtils.throwIf(!file.exists() || !file.isDirectory(), ErrorCode.SYSTEM_ERROR, "应用代码不存在，请先生成代码");

        //vue项目需要构建，然后部署
        if (codeGenType.equals(CodeGenTypeEnum.VUE_PROJECT.getValue())) {
            ThrowUtils.throwIf(!buildVueProject.buildVueProject(savePath), ErrorCode.OPERATION_ERROR, "系统错误，请联系管理员查看日志！");
            //这里要将dist目录作为savePath
            file = new File(savePath, "dist");
        }


        //将应用代码从code目录下复制到deploy目录下
        //D:\Idea_workspace\2025\third-ai-code-generate\tmp\deploy\desd584
        String deployPath = AppConstant.DEPLOY_PATH + deployKey;


        try {
            FileUtil.copyContent(file, new File(deployPath), true);
        } catch (IORuntimeException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文件复制失败！");
        }

        //更新数据库
        App appUpd = new App();
        appUpd.setId(appId);
        appUpd.setDeployKey(deployKey);
        appUpd.setDeployedTime(LocalDateTime.now());
        ThrowUtils.throwIf(!updateById(appUpd), ErrorCode.OPERATION_ERROR);


        String deployAccessFulPath = AppConstant.DEPLOY_HOST + deployKey;

        //生成项目的封面图 并且保存到数据库 异步操作
        generateAppScreenShotAsync(deployAccessFulPath, appId);

        //返回访问路径
        return ResultUtils.success(deployAccessFulPath);
    }

    private void generateAppScreenShotAsync(String deployAccessFulPath, Long appId) {
        try {
            new Thread(() -> {
                String result = screenShotsService.generateAndUploadScreenshot(deployAccessFulPath);
                App app = new App();
                app.setId(appId);
                app.setCover(result);
                this.updateById(app);
            }).start();
        } catch (Exception e) {
            log.error("异步生成并保存项目封面错误：", e);
        }
    }

    /**
     * 重写 mybatis - flux 的删除方法
     *
     * @param id
     * @return
     */
    @Override
    public boolean removeById(Serializable id) {
        //删除应用前，首先删除该id对应的聊天记录
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("appId", id);
        chatHistoryService.remove(queryWrapper);
        return super.removeById(id);
    }
}
